Evaluating CLM Simulations at NEON Tower Sites – Tutorial – Interactive Plots

This tutorial provides a tool for interacive evaluations and analysis of CLM at NEON tower sites. This tool is based on Bokeh python package.

⚠️ Note: Before starting this tutorial, please make sure you successfully completed a simulation using the NEON_Simulation_Tutorial . Please use the same NEON site/sites here that you’ve already completed simulations for.


In this tutorial :

The notebook provide tools for interactive plotting and analysis of CLM at NEON tower sites. Below you will find steps to:

  1. Create interactive app for showing tseries

  2. Create interactive app for scatter plots and statitical summary

Please use the same NEON site/sites that you’ve already completed the simulations for.

neon_sites = ["ABBY","BART", "BLAN", "CPER", "DCFS","DSNY"]
#neon_site = neon_sites[0]

Please make sure that you’ve succesfully completed the CLM simulations for these sites. When a simulation completes, the data are transferred to the archive directory.

Similar to the previous tutorials, you can check the history output files in the archive folder.

!ls /Users/negins/Desktop/Simulations/archive/{neon_sites[0]}.transient/lnd/hist/*2018*.nc |head -20
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-01.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-02.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-03.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-04.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-05.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-06.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-07.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-08.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-09.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-10.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-11.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h0.2018-12.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h1.2018-01-01-00000.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h1.2018-01-02-00000.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h1.2018-01-03-00000.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h1.2018-01-04-00000.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h1.2018-01-05-00000.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h1.2018-01-06-00000.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h1.2018-01-07-00000.nc
/Users/negins/Desktop/Simulations/archive/ABBY.transient/lnd/hist/ABBY.transient.clm2.h1.2018-01-08-00000.nc

1- Import Python Libraries

Run the below code to import the required python libraries for this notebook:

#Import Libraries
%matplotlib inline

import os
import sys
import time

import numpy as np
import pandas as pd
import xarray as xr

from glob import glob
from os.path import join, expanduser

import matplotlib
import matplotlib.pyplot as plt

from scipy import stats

from neon_utils import download_eval_files
#Specify the year
year = "2018"

2- Load Pre-processed data:

df_list =[]
for neon_site in neon_sites[0:2]:

    pkl_dir = '/Users/negins/Desktop/Simulations/tutorials/processed_data'
    pkl_name = neon_site+'_'+year+'df_all.pkl'
    processed_data = os.path.join(pkl_dir,pkl_name)

    df_all = pd.read_pickle(processed_data).reset_index()
    df_all['site']=neon_site
    df_list.append(df_all)
    
    
df_all_sites = pd.concat(df_list)
print (df_all_sites)

print (df_all_sites[df_all_sites['site']=='ABBY'])
       index                time       NEE        FSH  EFLX_LH_TOT  GPP  \
0          0 2018-01-01 00:00:00  1.592983  -6.384231     7.999955  NaN   
1          1 2018-01-01 00:30:00  1.180044 -19.939597     9.523534  NaN   
2          2 2018-01-01 01:00:00  0.975515 -16.453217     9.358360  NaN   
3          3 2018-01-01 01:30:00  0.260440  -1.420895     5.013022  NaN   
4          4 2018-01-01 02:00:00  2.474247  -4.324410     2.368721  NaN   
...      ...                 ...       ...        ...          ...  ...   
17515  17515 2018-12-31 21:30:00 -1.981586  -3.769276     0.499174  NaN   
17516  17516 2018-12-31 22:00:00 -1.981586  -6.985562     0.194687  NaN   
17517  17517 2018-12-31 22:30:00 -1.947493  -2.467863    -0.841958  NaN   
17518  17518 2018-12-31 23:00:00 -2.057455   0.689793     0.356680  NaN   
17519  17519 2018-12-31 23:30:00 -1.947493   1.180507    -0.026794  NaN   

        Rnet      sim_FCEV      sim_FCTR  sim_FGEV  ...        sim_AR  \
0     -49.70 -7.638256e+00  1.545730e-16  6.177156  ...  1.796636e-05   
1     -64.01 -9.999990e+00  0.000000e+00  8.121943  ...  2.111855e-05   
2     -67.15 -8.272110e+00  0.000000e+00  7.193620  ...  2.045357e-05   
3     -62.43 -7.173703e+00  0.000000e+00  6.415617  ...  2.017028e-05   
4     -67.24 -7.002048e+00  0.000000e+00  6.262212  ...  1.964822e-05   
...      ...           ...           ...       ...  ...           ...   
17515 -28.58  1.027028e-07  0.000000e+00  0.144563  ...  9.833552e-07   
17516  -8.33 -1.377483e-08  9.712527e-17  0.036043  ...  9.832469e-07   
17517  -6.71  1.069803e-05  0.000000e+00  8.786987  ...  9.747794e-07   
17518  -5.18  3.430142e-07  3.363154e-17  0.141638  ...  9.628571e-07   
17519  -1.90           NaN           NaN       NaN  ...           NaN   

         sim_HR  sim_EFLX_LH_TOT   sim_Rnet   sim_NEE  year  month  day  hour  \
0      0.000026        -1.461100 -44.383476 -0.000015  2018      1    1     0   
1      0.000025        -1.878047 -55.759464 -0.000046  2018      1    1     0   
2      0.000025        -1.078490 -57.324272 -0.000046  2018      1    1     1   
3      0.000025        -0.758087 -53.428955 -0.000045  2018      1    1     1   
4      0.000025        -0.739836 -57.476784 -0.000044  2018      1    1     2   
...         ...              ...        ...       ...   ...    ...  ...   ...   
17515  0.000002         0.144563 -33.488106 -0.000003  2018     12   31    21   
17516  0.000002         0.036043 -12.458500 -0.000003  2018     12   31    22   
17517  0.000002         8.786998 -10.466762 -0.000003  2018     12   31    22   
17518  0.000002         0.141638  -9.539567 -0.000003  2018     12   31    23   
17519       NaN              NaN        NaN       NaN  2018     12   31    23   

       site  
0      ABBY  
1      ABBY  
2      ABBY  
3      ABBY  
4      ABBY  
...     ...  
17515  BART  
17516  BART  
17517  BART  
17518  BART  
17519  BART  

[35040 rows x 24 columns]
       index                time       NEE        FSH  EFLX_LH_TOT  GPP  \
0          0 2018-01-01 00:00:00  1.592983  -6.384231     7.999955  NaN   
1          1 2018-01-01 00:30:00  1.180044 -19.939597     9.523534  NaN   
2          2 2018-01-01 01:00:00  0.975515 -16.453217     9.358360  NaN   
3          3 2018-01-01 01:30:00  0.260440  -1.420895     5.013022  NaN   
4          4 2018-01-01 02:00:00  2.474247  -4.324410     2.368721  NaN   
...      ...                 ...       ...        ...          ...  ...   
17515  17515 2018-12-31 21:30:00 -3.305683  53.417014   127.891286  NaN   
17516  17516 2018-12-31 22:00:00 -2.321932  24.107347     6.909593  NaN   
17517  17517 2018-12-31 22:30:00 -1.665410  35.354257    -0.758793  NaN   
17518  17518 2018-12-31 23:00:00 -3.370616  -0.794320    -2.582525  NaN   
17519  17519 2018-12-31 23:30:00 -1.899204  -1.785594    -6.315270  NaN   

         Rnet   sim_FCEV      sim_FCTR  sim_FGEV  ...    sim_AR    sim_HR  \
0      -49.70  -7.638256  1.545730e-16  6.177156  ...  0.000018  0.000026   
1      -64.01  -9.999990  0.000000e+00  8.121943  ...  0.000021  0.000025   
2      -67.15  -8.272110  0.000000e+00  7.193620  ...  0.000020  0.000025   
3      -62.43  -7.173703  0.000000e+00  6.415617  ...  0.000020  0.000025   
4      -67.24  -7.002048  0.000000e+00  6.262212  ...  0.000020  0.000025   
...       ...        ...           ...       ...  ...       ...       ...   
17515  280.43  37.526901  8.447243e+00  0.421997  ...  0.000022  0.000026   
17516  235.71  36.608852  7.772662e+00  0.761458  ...  0.000021  0.000026   
17517  185.06  31.220009  7.147894e+00  0.631587  ...  0.000021  0.000026   
17518  127.01  25.218470  5.901747e+00  0.661987  ...  0.000021  0.000026   
17519   70.92        NaN           NaN       NaN  ...       NaN       NaN   

       sim_EFLX_LH_TOT    sim_Rnet   sim_NEE  year  month  day  hour  site  
0            -1.461100  -44.383476 -0.000015  2018      1    1     0  ABBY  
1            -1.878047  -55.759464 -0.000046  2018      1    1     0  ABBY  
2            -1.078490  -57.324272 -0.000046  2018      1    1     1  ABBY  
3            -0.758087  -53.428955 -0.000045  2018      1    1     1  ABBY  
4            -0.739836  -57.476784 -0.000044  2018      1    1     2  ABBY  
...                ...         ...       ...   ...    ...  ...   ...   ...  
17515        46.396141  259.828949  0.000015  2018     12   31    21  ABBY  
17516        45.142975  218.192841  0.000012  2018     12   31    22  ABBY  
17517        38.999493  167.951035  0.000011  2018     12   31    22  ABBY  
17518        31.782204  110.927956  0.000007  2018     12   31    23  ABBY  
17519              NaN         NaN       NaN  2018     12   31    23  ABBY  

[17520 rows x 24 columns]
df_all = df_all_sites
#-- extract year, month, day, hour information from time
df_all['year'] = df_all['time'].dt.year
df_all['month'] = df_all['time'].dt.month
df_all['day'] = df_all['time'].dt.day
df_all['hour'] = df_all['time'].dt.hour

3) Time-Series Visualization

3.1) Time-Series Dashboard with Scatterplots

#time-series with Dropdown menu for values
from scipy import stats

import yaml
from bokeh.themes import Theme
from bokeh.models import ColumnDataSource, Slider , Dropdown, Select, PreText, Label, Slope
from bokeh.layouts import row,column
# make a simple plot time-series

from bokeh.io import output_notebook, show
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool
from neon_bokeh_utils import simple_tseries

import os
os.environ['BOKEH_RESOURCES'] = 'inline'

from bokeh.resources import INLINE
import bokeh.io
from bokeh import *
bokeh.io.output_notebook(INLINE)

#time-series with Dropdown menu for values
from scipy import stats

import yaml
from bokeh.themes import Theme
from bokeh.models import ColumnDataSource, Slider , Dropdown, Select, PreText, Label, Slope
from bokeh.layouts import row,column
# make a simple plot time-series

from bokeh.io import output_notebook, show
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, HoverTool
#from neon_bokeh_utils import simple_tseries

import os
os.environ['BOKEH_RESOURCES'] = 'inline'

from bokeh.resources import INLINE
import bokeh.io
from bokeh import *
bokeh.io.output_notebook(INLINE)
Loading BokehJS ...
Loading BokehJS ...
Loading BokehJS ...
freq_list = ['all','hourly','daily','monthly']
def get_data (df, var, freq, site):
    df[df['site']==neon_site]
    #var_name = var
    sim_var_name = "sim_"+var
    #print (var)
    #print (sim_var_name)
    if freq=="monthly":
        df = df.groupby(['year','month']).mean().reset_index()
        df["day"]=15
        df['time']=pd.to_datetime(df[["year", "month","day"]])

    elif freq=="daily":
        df = df.groupby(['year','month','day']).mean().reset_index()
        df['time']=pd.to_datetime(df[["year", "month", "day"]])

    elif freq=="hourly" or freq=="all":
        df = df.groupby(['year','month','day','hour']).mean().reset_index()
        df['time']=pd.to_datetime(df[["year", "month", "day","hour"]])
    
    
    df_new = pd.DataFrame({'time':df['time'],'NEON':df[var],'CLM':df[sim_var_name]})
    #print(df_new)
    return df_new

def find_regline(df, var, sim_var_name):
        # find the trendline:
        #sim_var_name = "sim_"+var
        #print (var)
        #print (sim_var_name)

        df_temp = df[[var, sim_var_name]]#.dropna()
        
        #df_temp = pd.DataFrame(df, columns)
        df_temp.dropna(inplace=True)
        #print (df_temp)

        #z = np.polyfit(df_temp[var], df_temp[sim_var_name], 1)
        #p = np.poly1d(z)
        
        #-----
        slope, intercept, r_value, p_value, std_err = stats.linregress(df_temp[var], df_temp[sim_var_name])
        return slope, intercept, r_value, p_value, std_err
    
    
    
plot_vars =['FSH','EFLX_LH_TOT','Rnet','NEE','GPP']
def simple_tseries(doc):
    
    df_new = get_data(df_all, 'EFLX_LH_TOT','hourly','ABBY')
    
    source = ColumnDataSource(df_new)

    #-- what are tools options
    tools = "hover, box_zoom, undo, crosshair"

    p = figure(tools=tools, x_axis_type="datetime", title= "Neon Time-Series "+neon_site)

    
    p.line('time', 'NEON', source=source, alpha=0.8, line_width=4, color="navy", legend_label="NEON")
    p.line('time', 'CLM',source=source , alpha=0.8, line_width=3, color="red", legend_label="CLM")
    #p.circle('time', 'var', source=source, alpha=0.8, color="navy")

    p.xaxis.major_label_text_color = 'dimgray'
    p.xaxis.major_label_text_font_size = '15px'
    p.yaxis.major_label_text_color = 'dimgray'
    p.yaxis.major_label_text_font_size = '15px'
    
    p.xaxis.axis_label_text_font_size = "15pt"
    p.axis.axis_label_text_font_style = "bold"
    p.grid.grid_line_alpha = 0.5
    p.title.text_font_size = '15pt'
    p.xaxis.axis_label = 'Time'


    
    def scatter_plot(q):
        q.circle('NEON', 'CLM', source=source, alpha=0.8, color="navy",fill_alpha=0.2, size=10)

        q.xaxis.major_label_text_color = 'dimgray'
        q.xaxis.major_label_text_font_size = '15px'
        q.yaxis.major_label_text_color = 'dimgray'
        q.yaxis.major_label_text_font_size = '15px'
    
        q.xaxis.axis_label_text_font_size = "13pt"
        q.yaxis.axis_label_text_font_size = "13pt"

        q.axis.axis_label_text_font_style = "bold"
        q.grid.grid_line_alpha = 0.5
        q.title.text_font_size = '15pt'
        q.xaxis.axis_label = 'NEON'
        q.yaxis.axis_label = 'CLM'

        
        #x = range(0,500)
        #y = range(0,500)

        #q.line(x, y,alpha=0.8, line_width=4, color="gray")
        
    
    q_width = 350
    q_height = 350
    q = figure(tools=tools,width=350, height=350)
    scatter_plot(q)
    
    p.add_tools(
        HoverTool(
            tooltips=[('value','@value{2.2f}'), 
                      ('index', '@index')]
        )
    )
    
    stats = PreText(text='', width=500)

    
    menu = Select(options=plot_vars,value='EFLX_LH_TOT', title='Variable') 
    
    def update_variable (attr, old, new):
        
        #print (menu.value)
        #print (menu_freq.value)
        df_new = get_data(df_all, menu.value, menu_freq.value, menu_site.value)

        #slope, intercept, r_value, p_value, std_err = find_regline(df_new, 'NEON','CLM')
        #print (r_value)
        #slope_label = "y="+"{:.2f}".format(slope)+"+"+"{:.2f}".format(intercept)+"x"+" (R2="+"{:.2f}".format(r_value)+")"
        #mytext = Label(text=slope_label , x=0+20, y=q_height-100, 
        #       x_units="screen", y_units='screen', text_align="left")
        
        #regression_line = Slope(gradient=slope, y_intercept=intercept, line_color="red")

        #print(slope_label)
        #q.add_layout(mytext)
        #q.add_layout(regression_line)
        
        
        source.data =df_new
        #source.stream(df_new)


    def update_site (attr, old, new):
        p.title.text = "Neon Time-Series " +menu_site.value

    menu.on_change('value', update_variable)


    menu_freq = Select(options=freq_list,value='all', title='Frequency') 

    menu_freq.on_change('value', update_variable)


    menu_site = Select(options=neon_sites,value='ABBY', title='Neon Site') 
    menu_site.on_change('value', update_site)

    
    #layout = row(column(menu, menu_freq, menu_site, q),  p)
    layout = row(p, column( menu, menu_freq, menu_site, q))
    doc.add_root(layout)
    
    doc.theme = Theme(json=yaml.load("""
        attrs:
            Figure:
                background_fill_color: "#FFFFFF"
                outline_line_color: white
                toolbar_location: above
                height: 550
                width: 1100
            Grid:
                grid_line_dash: [6, 4]
                grid_line_color: gray
    """, Loader=yaml.FullLoader))
    
output_notebook()

show(simple_tseries, notebook_handle=True)
Loading BokehJS ...

3.2) Time-Series DashBoard with Statistics

def stats_tseries(doc):
    
    df_new = get_data(df_all, 'EFLX_LH_TOT','hourly','ABBY')
    
    source = ColumnDataSource(df_new)

    #-- what are tools options
    tools = "hover, box_zoom, undo, crosshair"

    def timeseries_plot (p):
        p.line('time', 'NEON', source=source, alpha=0.8, line_width=4, color="navy", legend_label="NEON")
        p.line('time', 'CLM',source=source , alpha=0.8, line_width=3, color="red", legend_label="CLM")
        #p.circle('time', 'var', source=source, alpha=0.8, color="navy")

        p.xaxis.major_label_text_color = 'dimgray'
        p.xaxis.major_label_text_font_size = '15px'
        p.yaxis.major_label_text_color = 'dimgray'
        p.yaxis.major_label_text_font_size = '15px'
    
        p.xaxis.axis_label_text_font_size = "15pt"
        p.axis.axis_label_text_font_style = "bold"
        p.grid.grid_line_alpha = 0.5
        p.title.text_font_size = '15pt'
        p.xaxis.axis_label = 'Time'

    
    def scatter_plot(q):
        q.circle('NEON', 'CLM', source=source, alpha=0.8, color="navy",fill_alpha=0.2, size=10)
        print (df_new)
        q.xaxis.major_label_text_color = 'dimgray'
        q.xaxis.major_label_text_font_size = '15px'
        q.yaxis.major_label_text_color = 'dimgray'
        q.yaxis.major_label_text_font_size = '15px'
    
        q.xaxis.axis_label_text_font_size = "13pt"
        q.yaxis.axis_label_text_font_size = "13pt"

        q.axis.axis_label_text_font_style = "bold"
        q.grid.grid_line_alpha = 0.5
        q.title.text_font_size = '15pt'
        q.xaxis.axis_label = 'NEON'
        q.yaxis.axis_label = 'CLM'
        
        slope, intercept, r_value, p_value, std_err = find_regline(df_new, 'NEON','CLM')
        #print (r_value)
        slope_label = "y="+"{:.2f}".format(slope)+"+"+"{:.2f}".format(intercept)+"x"+" (R2="+"{:.2f}".format(r_value)+")"
        mytext = Label(text=slope_label , x=0+20, y=q_height-100, 
                        x_units="screen", y_units='screen', text_align="left")
        
        regression_line = Slope(gradient=slope, y_intercept=intercept, line_color="red")

        #q.add_layout(mytext)
        q.add_layout(regression_line)
        print ("slope_label:", slope_label)
        q.title.text = slope_label


        
        #x = range(0,500)
        #y = range(0,500)

        #q.line(x, y,alpha=0.8, line_width=4, color="gray")
        
    
    p = figure(tools=tools, x_axis_type="datetime", title= "Neon Time-Series "+neon_site)
    timeseries_plot(p)
    
    q_width = 350
    q_height= 350
    q = figure(tools=tools,width=q_width, height=q_height)
    scatter_plot(q)
    
    p.add_tools(
        HoverTool(
            tooltips=[('value','@value{2.2f}'), 
                      ('index', '@index')]))
    

    
    def update_variable (attr, old, new):
        
        #print (menu.value)
        #print (menu_freq.value)
        df_new = get_data(df_all, menu.value, menu_freq.value, menu_site.value)
        
        #print ("=======================================")
        #print ("Statistics summary for var: "+menu.value)
        #print (df_new.describe())
        #print ("----------------")
        #print(slope_label)
        #print ("R2      = "+"{:.2f}".format(r_value))
        #print ("p-value = "+"{:.2f}".format(p_value))
        #print ("std-err = "+"{:.1f}".format(p_value))
        #print ("=======================================")

        
        #source.data =df_new
        source.data.update(df_new)


        #scatter_plot(q)
        #source.stream(df_new)
        #slope, intercept, r_value, p_value, std_err = find_regline(df_new, 'NEON','CLM')
        #print (r_value)
        #slope_label = "y="+"{:.2f}".format(slope)+"+"+"{:.2f}".format(intercept)+"x"+" (R2="+"{:.2f}".format(r_value)+")"
        #mytext = Label(text=slope_label , x=0+20, y=q_height-100, 
        #                x_units="screen", y_units='screen', text_align="left")
        
        #regression_line = Slope(gradient=slope, y_intercept=intercept, line_color="red")

        #q.add_layout(mytext)
        #print (q)
        #q.title.text = slope_label
        #q.add_layout(regression_line)
        #print ("slope_label:", slope_label)
        
        
        slope, intercept, r_value, p_value, std_err = find_regline(df_new, 'NEON','CLM')
        #print (r_value)
        slope_label = "y="+"{:.2f}".format(slope)+"+"+"{:.2f}".format(intercept)+"x"+" (R2="+"{:.2f}".format(r_value)+")"
        mytext = Label(text=slope_label , x=0+20, y=q_height-100, 
                        x_units="screen", y_units='screen', text_align="left")
        
        regression_line = Slope(gradient=slope, y_intercept=intercept, line_color="red")

        #q.add_layout(mytext)
        print ("slope_label:", slope_label)
        q.title.text = slope_label
        

    def update_site (attr, old, new):
        p.title.text = "Neon Time-Series " +menu_site.value

        
    menu = Select(options=plot_vars,value='EFLX_LH_TOT', title='Variable') 

    menu.on_change('value', update_variable)


    menu_freq = Select(options=freq_list,value='all', title='Frequency') 

    menu_freq.on_change('value', update_variable)


    menu_site = Select(options=neon_sites,value='HARV', title='Neon Site') 
    menu_site.on_change('value', update_site)

    
    #layout = row(column(menu, menu_freq, menu_site, q),  p)
    layout = row(p, column( menu, menu_freq, menu_site, q))
    doc.add_root(layout)
    
    doc.theme = Theme(json=yaml.load("""
        attrs:
            Figure:
                background_fill_color: "#FFFFFF"
                outline_line_color: white
                toolbar_location: above
                height: 550
                width: 1100
            Grid:
                grid_line_dash: [6, 4]
                grid_line_color: gray
    """, Loader=yaml.FullLoader))
output_notebook()

show(stats_tseries)
Loading BokehJS ...
                    time       NEON        CLM
0    2018-01-01 00:00:00   6.771990  10.186131
1    2018-01-01 01:00:00   6.005897  11.059019
2    2018-01-01 02:00:00   2.491490  11.179670
3    2018-01-01 03:00:00   0.852504   9.580622
4    2018-01-01 04:00:00   1.275915   8.094493
...                  ...        ...        ...
8755 2018-12-31 19:00:00   0.063862  16.354586
8756 2018-12-31 20:00:00  29.076470  18.078854
8757 2018-12-31 21:00:00  38.215214  21.416418
8758 2018-12-31 22:00:00   1.375882  23.241377
8759 2018-12-31 23:00:00  -2.141977  15.961921

[8760 rows x 3 columns]
slope_label: y=0.76+8.60x (R2=0.90)
/usr/local/Caskroom/miniconda/base/envs/neon3/lib/python3.9/site-packages/pandas/util/_decorators.py:311: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  return func(*args, **kwargs)

Congratulations! You have:

  • Created two time-seires dashboard for analyzing neon data.